package com.example.sunnyweather.logic

import androidx.lifecycle.liveData
import com.example.sunnyweather.logic.model.Weather
import com.example.sunnyweather.logic.network.SunnyWeatherNetwork
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import java.lang.RuntimeException
import kotlin.coroutines.CoroutineContext

object Repository {
    fun searchPlaces(query:String) = fire(Dispatchers.IO){
        val placeResponse = SunnyWeatherNetwork.searchPlaces(query)
        if(placeResponse.status == "ok"){
            val places = placeResponse.places
            Result.success(places)
        }else{
            Result.failure(RuntimeException("response status is ${placeResponse.status}"))
        }
    }

    fun refreshWeather(lng:String,lat:String)= fire(Dispatchers.IO) {
        //开启协程，使用async需要
        coroutineScope {
            //这里让两个请求同时进行，因为请求没有先后之分，所以为了保证后面有序，使用async函数
            //async函数会返回一个Deferred对象，只要在后面调用await()函数就能获得具体的返回值
            //而且，当调用await如果前面的代码还没执行完，那么就会阻塞当前协程，因此这里可以在后面用两个await，保证同时获得所有结果
            val deferredRealtime = async {
                SunnyWeatherNetwork.getRealtimeWeather(lng, lat)
            }
            val deferredDaily = async {
                SunnyWeatherNetwork.getDailyWeather(lng, lat)
            }
            val realtimeResponse = deferredRealtime.await()
            val dailyResponse = deferredDaily.await()

            if (realtimeResponse.status == "ok" && dailyResponse.status == "ok") {
                val weather = Weather(realtimeResponse.result.realtime, dailyResponse.result.daily)
                Result.success(weather)
            } else {
                Result.failure(
                    RuntimeException(
                        "realtime response status is ${realtimeResponse.status}\n" +
                                "daily response status is ${dailyResponse.status}"
                    )
                )
            }
        }
    }


    /**
     * 将每个函数中的 try-catch和emit提出来
     */
    private fun <T> fire(context:CoroutineContext,block : suspend () -> Result<T>) = liveData<Result<T>>(context) {
        val result = try{
            block()
        }catch (e:Exception){
            Result.failure<T>(e)
        }
        emit(result)
    }


}